package skyblue;

import java.util.LinkedList;
import java.util.Stack;
import java.util.Vector;

public class SkyBlue {

	private Vector<CycleSolver> cycleSolvers;
	private Stack<Constraint> unenforcedCns;
	private Stack<Variable> undetVars;
	private Stack<Constraint> execRootCns;
	private Stack<Variable> execRootVars;
	private Stack<Constraint> plan;
	// FM
	private LinkedList<Variable> subundetVars;

	public SkyBlue(Strength weakestStrength) {

		unenforcedCns = new Stack<Constraint>();
		execRootCns = new Stack<Constraint>();
		execRootVars = new Stack<Variable>();
		undetVars = new Stack<Variable>();
		plan = new Stack<Constraint>();
		subundetVars = new LinkedList<Variable>();
	}

	public SkyBlue(SkyBlue skyblue) {

		unenforcedCns = skyblue.getUnenforcedCns();
		execRootCns = skyblue.getExecRootCns();
		execRootVars = skyblue.getExecRootVars();
		undetVars = skyblue.getUndetVars();
		plan = skyblue.getPlan();
		subundetVars = skyblue.getSubundetVars();
	}

	public void addConstraint(Constraint cn) {

		Solver solver = new Solver(this);
		solver.addConstraint(cn);
	}

	public void removeConstraint(Constraint cn) {

		Solver solver = new Solver(this);
		solver.removeConstraint(cn);
	}

	public void changeConstraintStrength(Constraint cn, Strength strength) {

		Solver solver = new Solver(this);
		solver.changeConstraintStrength(cn, strength);
	}

	public void clearWorklist() {
		unenforcedCns.clear();
		execRootCns.clear();
		execRootVars.clear();
	}

	public void pushUnenforcedCn(Constraint cn) {
		unenforcedCns.push(cn);
	}

	public void addUndetVars(Variable var) {
		undetVars.push(var);
	}

	public void addSubundetVars(Variable var) {
		subundetVars.push(var);
	}

	public void addExecRoot(Constraint cn) {
		if (!execRootCns.contains(cn)) {
			execRootCns.push(cn);
			cn.backupSelectedMethod();
		}
	}

	public void addExecRoot(Variable var) {
		if (var.isValid()) {
			execRootVars.push(var);
		}
	}

	public void addToPlan(Constraint cn, Mark doneMark) {
		if (cn.enforced() && cn.getMark().getMyMark() != doneMark.getMyMark()) {
			cn.setMark(doneMark);
			Variable iteri;
			// add down stream of the constraint
			for (int i = 0; i < cn.getSelectedMethod().getOutputs().size(); i++) {
				iteri = cn.getSelectedMethod().getOutputs().get(i);
				addToPlan(iteri, doneMark);
			}
			this.plan.push(cn);
		}

	}

	public void addToPlan(Variable var, Mark doneMark) {
		// add the down stream of the variable to the stack of the constraints
		LinkedList<Constraint> cnlist = var.getConstraints();
		Constraint cn;
		while (!cnlist.isEmpty()) {
			cn = cnlist.pollLast();
			// FM
			if (cn.enforced()) {
				if (cn.getMark().getMyMark() != doneMark.getMyMark()) {
					if (!cn.getSelectedMethod().getOutputs().contains(var)) {
						addToPlan(cn, doneMark);
					} else if (cn.getSelectedMethod().getOutputs().size() > 1) {
						addToPlan(cn, doneMark);

					}
				}

			}
		}
	}

	public Vector<CycleSolver> getCycleSolvers() {
		return cycleSolvers;
	}

	public void setCycleSolvers(Vector<CycleSolver> cycleSolvers) {
		this.cycleSolvers = cycleSolvers;
	}

	public Stack<Constraint> getUnenforcedCns() {
		return unenforcedCns;
	}

	public void setUnenforcedCns(Stack<Constraint> unenforcedCns) {
		this.unenforcedCns = unenforcedCns;
	}

	public Stack<Variable> getUndetVars() {
		return undetVars;
	}

	public void setUndetVars(Stack<Variable> undetVars) {
		this.undetVars = undetVars;
	}

	public Stack<Constraint> getExecRootCns() {
		return execRootCns;
	}

	public void setExecRootCns(Stack<Constraint> execRootCns) {
		this.execRootCns = execRootCns;
	}

	public Stack<Variable> getExecRootVars() {
		return execRootVars;
	}

	public void setExecRootVars(Stack<Variable> execRootVars) {
		this.execRootVars = execRootVars;
	}

	public Stack<Constraint> getPlan() {
		return plan;
	}

	public void setPlan(Stack<Constraint> plan) {
		this.plan = plan;
	}

	public LinkedList<Variable> getSubundetVars() {
		return subundetVars;
	}

	public void setSubundetVars(LinkedList<Variable> subundetVars) {
		this.subundetVars = subundetVars;
	}
}
